require("Mind")

function UnitTests:testBehavior()

	local perpetratorBehaviorDescriptor =
	{
		Root = 
		{
			subGoals = 
			{
				"EliminateCrimeEvidences",
				--"EliminateCrimeWitnesses",
				--"BuildCrimeAliby",
				--"HideCrimeMotives"
			}
		},
		
		
		--- level 1 goals ----------------------------------------------------------------------------------------------------------------------------------------------------------------
		
		EliminateCrimeEvidences =
		{
			updateVariables = 
			function(goal)
			
				goal.variables.entitiesNames = {}
				goal.variables.entitiesType = "Prop"
				
				for entity in pairs(Entity.byTag.Evidence)
				do
					goal.variables.entitiesNames[#goal.variables.entitiesNames + 1] = entity.name
				end
			end,
			
			subGoals = 
			{
				"GetInformationAboutProps",
				"GoAndGetKnownProps",
				"BringPropsToHome",
			}
		},
		
		
		--- level 2 goals ----------------------------------------------------------------------------------------------------------------------------------------------------------------
		
		GetInformationAboutProps =
		{
			subGoals = 
			{
				"IntergotateInterlocutorsAboutProps",
				"InspectLocations",
			}
		},
		
		---
		
		GoAndGetKnownProps =
		{	
			causes = 
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "Know", doj = {"Prop", "@{entitiesNames}"}}
				}
			},
			
			subGoals = 
			{
				"GoAndGetKnownPropsFakeIntermediary",
			}
		},

		---
				
		GoAndGetKnownPropsFakeIntermediary =
		{		
			causes = 
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "Know", doj = {"Prop", "@{entitiesNames}"}}
				}
			},
			
			subGoals = 
			{
				"MoveToPhysicals",
				"PickupProps",
			}
		},
		
		---
		
		BringPropsToHome =
		{
			updateVariables = 
			function(goal)
			
				goal.variables.locationsNames = {"PerpetratorHome"}
				
				UnitTests.BringPropsToHomeVariables = goal.variables
			end,
					
			causes = 
			{
				s = {"Prop", "@{entitiesNames}"},
				p =
				{
					{ v = "isAt", doj = {"Location", "@myPockets"}}
				}
			},
			
			subGoals = 
			{
				"MoveToLocations",
				"DropPropsInPocketToLocations",
			}
		},
		
		
		--- level 3 goals ----------------------------------------------------------------------------------------------------------------------------------------------------------------
		
		IntergotateInterlocutorsAboutProps =
		{
		},
		
		---
		
		InspectLocations =
		{
		},
		
		
		--- effector level ----------------------------------------------------------------------------------------------------------------------------------------------------------------
	
		MoveToPhysicals =
		{
			constraint =
			{
				type					= "intersectionOfSet1EntityWithOneOfSet2Entities",
				set1EntitiesType		= "@myBodyType",
				set1EntitiesNames		= "@myBodyName",
				set2EntitiesType		= "@entitiesType",
				set2EntitiesNames		= "@{entitiesNames}",
				property				= "CurrentContainer",
			},
			
			effects =
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "isBesidePhysical", doj = {"@entitiesType", "@{entitiesNames}"}}
				}
			},
		},
		
		---
		
		PickupProps =
		{
			causes = 
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "isBesidePhysical", doj = {"@entitiesType", "@{entitiesNames}"}}
				}
			},
			
			effects =
			{
				s = {"Prop", "@{entitiesNames}"},
				p =
				{
					{ v = "isAt", doj = {"Location", "@myPockets"}}
				}
			},
		},
		
		---
	
		MoveToLocations =
		{	
			constraint =
			{
				type					= "intersectionOfSet1EntityWithSet2Values",
				set1EntitiesType		= "@myBodyType",
				set1EntitiesNames		= "@myBodyName",
				set2ValuesEntitiesType	= "Location",
				set2ValuesEntitiesNames	= "@{locationsNames}",
				property				= "CurrentContainer",
			},
			
			effects =
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "isAt", doj = {"Location", "@{locationsNames}"}}
				}
			},
		},
		
		---
		
		DropPropsInPocketToLocations =
		{
			causes =
			{
				s = {"@myType", "@myName"},
				p =
				{
					{ v = "isAt", doj = {"Location", "@{locationsNames}"}}
				}
			},	
			
			effects =
			{
				s = {"Prop", "@{entitiesNames}"},
				p =
				{
					{ v = "isAt", doj = {"Location", "@{locationsNames}"}}
				}
			},				
		},
	}
	
	---
	
	local stage =
	{
		Rooms =
		{
			Street =
			{			
			},
			
			PerpetratorHome =
			{	
			},
						
			PerpetratorGarden =
			{	
			},
						
			BackStreet =
			{			
			},
		},
		
		Doors =
		{
			{
				Rooms = 
				{
					"Street",
					"PerpetratorHome"
				},
				
				ClosedAndLocked = true			
			},
			
			{
				Rooms = 
				{
					"PerpetratorHome",
					"PerpetratorGarden"
				},
				
				ClosedAndLocked = true			
			},
			
			{
				Rooms = 
				{
					"PerpetratorGarden",
					"BackStreet"
				},
				
				ClosedAndLocked = true			
			}
		}		
	}
	
	local cast =
	{
		Player =
		{
			initialLocation = "Street"
		},
	
		Bots=
		{
			Perpetrator =
			{
				behavior = perpetratorBehaviorDescriptor
			}
		}
	}
	
	local game = Game:new(stage, cast)
	
	
	-- test goal variables inheritence

	local perpetrator = gField.entityTypes.Bot.Perpetrator
	local perpetratorBody = perpetrator.body
	local rootGoal = perpetrator.behavior.rootGoal
	
	local perpetratorHome = gField.entityTypes.Location.PerpetratorHome
	local street = gField.entityTypes.Location.Street
	local perpetratorGarden = gField.entityTypes.Location.PerpetratorGarden
	local backStreet = gField.entityTypes.Location.BackStreet
	
	assert(rootGoal.name == "Root")
	
	local intergotateInterlocutorsAboutPropsGoal = 
		rootGoal.subGoals.EliminateCrimeEvidences.subGoals.GetInformationAboutProps.subGoals.IntergotateInterlocutorsAboutProps
	
	assert(intergotateInterlocutorsAboutPropsGoal.name == "IntergotateInterlocutorsAboutProps")
	
	rootGoal.variables.testVariableInRoot = "toto"
	intergotateInterlocutorsAboutPropsGoal.variables.testVariableInSubSubGoal = "toto"
	
	assert(
		intergotateInterlocutorsAboutPropsGoal.variables.testVariableInSubSubGoal == 
		intergotateInterlocutorsAboutPropsGoal.variables.testVariableInRoot				-- this variable is inherited from parent parent goal
	)
	
	
	-- create props
	
	local prop1 = Prop:new("prop1")
	local prop2 = Prop:new("prop2")
	prop2:addTag("tagExemple")
	local evidence1 = Prop:new("evidence1")
	evidence1:addTag("Evidence")
	local evidence2 = Prop:new("evidence2")
	evidence2:addTag("Evidence")
	local evidence3 = Prop:new("evidence3")
	evidence3:addTag("Evidence")


	-- set entities position to satisfy the goal constraints
	 
	evidence1:collapseProperty("CurrentContainer", perpetratorHome)
	evidence2:collapseProperty("CurrentContainer", street)
	evidence3:collapseProperty("CurrentContainer", perpetratorHome)
	perpetratorBody:collapseProperty("CurrentContainer", perpetratorHome)
	

	-- test goal variable update
	
	gGame:start()
	
	local moveToPhysicalsGoal = 
		rootGoal.subGoals.EliminateCrimeEvidences.subGoals.GoAndGetKnownProps.subGoals.GoAndGetKnownPropsFakeIntermediary.subGoals.MoveToPhysicals
		
	assert(#moveToPhysicalsGoal.variables.entitiesNames == 3)
	
	for i, entityName in ipairs(moveToPhysicalsGoal.variables.entitiesNames)
	do
		assert(gField.entityTypes[moveToPhysicalsGoal.variables.entitiesType][entityName]:hasTag("Evidence"))
	end
	
	
	-- test standard goal variables
	
	assert(moveToPhysicalsGoal.variables.me == perpetrator)
	assert(moveToPhysicalsGoal.variables.myBody == perpetrator.body)
	assert(moveToPhysicalsGoal.variables.myPockets == perpetrator.body.pockets)
	
	
	-- test intentions
	
	gDramaManager.intentionsManager:addIntention{
	
		s = {"Bot", "Perpetrator"},
		p =
		{
			{ v = "isAt", doj = {"Location", "CrimeScene"}}
		}
	}
	local intention = gDramaManager.intentionsManager:addIntention{
	
		s = {"Bot", "Perpetrator"},
		p =
		{
			{ v = "isAt", doj = {"Location", "PerpetratorHome"}}
		}	
	}
	
	gPlayer.body:execute("moveTo", perpetratorHome)
	--Logger.print = true
	gGame:onBeat()
	--Logger.print = false
	
	assert(not intention:isRealized())
	
	---
	
	perpetrator:addBelief(
		{ 
			s = {"Bot", "Perpetrator"},
			p =
			{
				{ v = "Know", doj = {"Prop", "evidence2"}}
			}		
		}, 
		true
	)
	
	gPlayer.body:execute("moveTo", street)
	gGame:onBeat()
	gPlayer.body:execute("moveTo", perpetratorHome)
	--Logger.print = true
	gGame:onBeat()
	--Logger.print = false
	
	assert(intention:isRealized())
	
	
	--- test goal constraints
	
	Logger.print = true
	
	gRandom:addContextualBias({"Prop", "evidence1", "CurrentContainer"}, backStreet)
	gRandom:addContextualBias({"Prop", "evidence2", "CurrentContainer"}, backStreet)
	gRandom:addContextualBias({"Prop", "evidence3", "CurrentContainer"}, backStreet)
	gRandom:addContextualBias({"Body", "PerpetratorBody", "CurrentContainer"}, backStreet)
	
	evidence1.properties.CurrentContainer[perpetratorHome] = 1
	evidence1.properties.CurrentContainer[street] = nil
	evidence1.properties.CurrentContainer[perpetratorGarden] = 1
	evidence1.properties.CurrentContainer[backStreet] = nil
	
	evidence2.properties.CurrentContainer[perpetratorHome] = 1
	evidence2.properties.CurrentContainer[street] = 1
	evidence2.properties.CurrentContainer[perpetratorGarden] = nil
	evidence2.properties.CurrentContainer[backStreet] = nil
	
	evidence3.properties.CurrentContainer[perpetratorHome] = 1
	evidence3.properties.CurrentContainer[street] = 1
	evidence3.properties.CurrentContainer[perpetratorGarden] = 1
	evidence3.properties.CurrentContainer[backStreet] = nil
	
	perpetratorBody.properties.CurrentContainer[perpetratorHome] = nil
	perpetratorBody.properties.CurrentContainer[street] = 1
	perpetratorBody.properties.CurrentContainer[perpetratorGarden] = 1
	perpetratorBody.properties.CurrentContainer[backStreet] = 1
	
	UnitTests.BringPropsToHomeVariables.locationsNames = 
	{
		"PerpetratorHome",
		"Street",
		"PerpetratorGarden",
		"BackStreet"
	}
				
	intention:tryToRealize()
	assert(intention:isRealized())
	
	assert(not intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][perpetratorHome])
	assert(intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][street])
	assert(intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][perpetratorGarden])
	assert(not intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][backStreet])		-- removed because not existing in any evidences entities

	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][perpetratorHome])	-- removed because not exisiting in perpetrator body
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][street])
	assert(intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][perpetratorGarden])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][backStreet])	

	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][perpetratorHome])	-- removed because not exisiting in perpetrator body
	assert(intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][street])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][perpetratorGarden])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][backStreet])
	
	---
		
	evidence1.properties.CurrentContainer[perpetratorHome] = nil
	evidence1.properties.CurrentContainer[street] = 1
	evidence1.properties.CurrentContainer[perpetratorGarden] = 1
	evidence1.properties.CurrentContainer[backStreet] = 1
	
	evidence2.properties.CurrentContainer[perpetratorHome] = 1
	evidence2.properties.CurrentContainer[street] = 1
	evidence2.properties.CurrentContainer[perpetratorGarden] = 1
	evidence2.properties.CurrentContainer[backStreet] = 1
	
	evidence3.properties.CurrentContainer[perpetratorHome] = 1
	evidence3.properties.CurrentContainer[street] = 1
	evidence3.properties.CurrentContainer[perpetratorGarden] = 1
	evidence3.properties.CurrentContainer[backStreet] = 1
	
	perpetratorBody.properties.CurrentContainer[perpetratorHome] = 1
	perpetratorBody.properties.CurrentContainer[street] = 1
	perpetratorBody.properties.CurrentContainer[perpetratorGarden] = 1
	perpetratorBody.properties.CurrentContainer[backStreet] = 1
	
	UnitTests.BringPropsToHomeVariables.locationsNames = 
	{
		"PerpetratorHome",
		"PerpetratorGarden",
	}
				
	intention:tryToRealize()
	assert(intention:isRealized())
	
	assert(intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][perpetratorHome])
	assert(not intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][street])
	assert(intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][perpetratorGarden])
	assert(not intention.constraintTrackingTable.myBodyName.CurrentContainer[perpetratorBody][backStreet])

	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][perpetratorHome])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][street])
	assert(intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][perpetratorGarden])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence1][backStreet])	

	assert(intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][perpetratorHome])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][street])
	assert(intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][perpetratorGarden])
	assert(not intention.constraintTrackingTable.entitiesNames.CurrentContainer[evidence2][backStreet])	
		
	Logger.print = false
end